#include "rgblinkProtocol.h"
#include <string.h>
#include "systick.h"
#include <stdio.h>
#include <windows.h>
#include "util.h"


typedef enum 
{
	interpret_cmd_init,
	interpret_cmd_start,
	interpret_cmd_data,
	interpret_cmd_end,
} InterpretCmdState_TypeDef;

static const char cmd_header[] = {'<','F'};
static const char cmd_end[] = {'>'};




bool interpret_receive_char(InterpretStateType * protocol, BYTE value)
{
	
	if(value == '<')
	{
		protocol->state = interpret_cmd_init;
	}
	
	switch(protocol->state)
	{
	case interpret_cmd_init:
		if(value == '<')
		{
			protocol->index = 1;
			protocol->state = interpret_cmd_start;
		}
		break;
	case interpret_cmd_start:		
		if(value == cmd_header[protocol->index++])
		{
			if(protocol->index >= sizeof(cmd_header))
			{
				protocol->state = interpret_cmd_data;
				protocol->index = 0;
			}
		}
		else
		{
			protocol->state = interpret_cmd_init;
			//DebugPutString("Preamp Start Header error\r\n");
		}
		break;
	case interpret_cmd_data:
		if((protocol->index & 0x01) == 0)
		{
			protocol->hexChar[0] = value;
			++protocol->index;
			break;
		}
		if(protocol->index/2 >= CMD_INDEX_SIZE)
		{
			protocol->state = interpret_cmd_init;
		}
		else
		{
			protocol->hexChar[1] = value;
			protocol->cmd[protocol->index/2] = HexCharToByte((char *) protocol->hexChar);
			++protocol->index;
			if(protocol->index/2 >= CMD_INDEX_SIZE)
			{
				protocol->state = interpret_cmd_end;
				protocol->index = 0;
			}
		}
		break;
	case interpret_cmd_end:
		if(value == cmd_end[protocol->index])
		{
			++protocol->index;
			if(protocol->index >= sizeof(cmd_end))
			{
				protocol->state = interpret_cmd_init;
				protocol->index = 0;

				return TRUE;
			}
		}
		else
		{
			protocol->state = interpret_cmd_init;			
		}
		break;
	default:
		protocol->state = interpret_cmd_init;
		break;
	}
	return FALSE;
	
}



static bool _interpret_cmd_char(RGBlinkProtocol * protocol, u8 value)
{
	
	if(value == '<')
	{
		protocol->state = interpret_cmd_init;
	}
	
	switch(protocol->state)
	{
	case interpret_cmd_init:
		if(value == '<')
		{
			protocol->index = 1;
			protocol->state = interpret_cmd_start;
		}
		break;
	case interpret_cmd_start:		
		if(value == cmd_header[protocol->index++])
		{
			if(protocol->index >= sizeof(cmd_header))
			{
				protocol->state = interpret_cmd_data;
				protocol->index = 0;
			}
		}
		else
		{
			protocol->state = interpret_cmd_init;
			//DebugPutString("Preamp Start Header error\r\n");
		}
		break;
	case interpret_cmd_data:
		if((protocol->index & 0x01) == 0)
		{
			protocol->hexCharBuffer[0] = value;
			++protocol->index;
			break;
		}
		if(protocol->index/2 >= CMD_INDEX_SIZE)
		{
			protocol->state = interpret_cmd_init;
		}
		else
		{
			protocol->hexCharBuffer[1] = value;
			protocol->receiveBuffer[protocol->index/2] = HexCharToByte((char *) protocol->hexCharBuffer);
			++protocol->index;
			if(protocol->index/2 >= CMD_INDEX_SIZE)
			{
				protocol->state = interpret_cmd_end;
				protocol->index = 0;
			}
		}
		break;
	case interpret_cmd_end:
		if(value == cmd_end[protocol->index])
		{
			++protocol->index;
			if(protocol->index >= sizeof(cmd_end))
			{
				int i;
				int add = protocol->receiveBuffer[CMD_INDEX_ADDRESS];
				protocol->state = interpret_cmd_init;
				protocol->index = 0;

				if(add >= MAX_RGBLNK_DEVICE)
				{
					break;
				}
				
				protocol->receiveSn[add] = protocol->receiveBuffer[CMD_INDEX_SN];
				for(i=0; i<CMD_INDEX_SIZE; i++)
				{
					protocol->receiveCmd[add][i] = protocol->receiveBuffer[i];
				}
				
				protocol->isConnect[add] = 1;
				
				
				// active cmd
				protocol->callback(protocol->receiveBuffer);

				return TRUE;
			}
		}
		else
		{
			protocol->state = interpret_cmd_init;			
		}
		break;
	default:
		protocol->state = interpret_cmd_init;
		break;
	}
	return FALSE;
	
}


void Task_RGBlinkProtocol(RGBlinkProtocol * protocol)
{
	u8 value;
	while(pipe_read(protocol->pipe, &value))
	{
		if(_interpret_cmd_char(protocol, value))
		{
			break;
		}		
	}
		
}


void Init_RGBlinkProtocol(RGBlinkProtocol * protocol, Pipe_t *pipe, RGBlinkProtocolCallback callback, SendBufferFunc sendBuffer )
{
	int i;
	protocol->pipe = pipe;
	protocol->callback = callback;
	protocol->index = 0;
	protocol->state = interpret_cmd_init;
	protocol->sendBuffer = sendBuffer;
	for(i=0; i<MAX_RGBLNK_DEVICE; i++)
	{
		protocol->receiveSn[i]  = 0;
		protocol->sendSn[i] = 0;
		protocol->isConnect[i] = 0;
		
	}
}

void _send_cmd_rgblink(RGBlinkProtocol * protocol, int address, BYTE cmd, BYTE dat1, BYTE dat2, BYTE dat3, BYTE dat4)
{
	char buffer[20];
	BYTE checksum = 0;

	
	
	++protocol->sendSn[address];
	
	checksum = protocol->sendSn[address]+address+cmd+dat1+dat2+dat3+dat4;
	sprintf(buffer, "<T%02x%02x%02x%02x%02x%02x%02x%02x>", 
		address, protocol->sendSn[address], cmd, dat1, dat2, dat3, dat4, checksum);

	protocol->sendBuffer(buffer, strlen(buffer));
}

bool canSendCmd_rgblink(RGBlinkProtocol * protocol, int add)
{
	int receive_sn;
	int send_sn;

	receive_sn = protocol->receiveSn[add];
	send_sn = protocol->sendSn[add];

	//printf("canSendCmd_rgblink_%d:",add);
	//printf("receive_sn = %d,send_sn = %d\r\n",receive_sn,send_sn);
	if(receive_sn <= send_sn)
	{
		if((send_sn-receive_sn)<10)
		{
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}
	else
	{
		if((send_sn + (256-receive_sn)) < 10)
		{
			return TRUE;			
		}
		else
		{
			return FALSE;
		}
	}
}

int  SendCmdNoWait_RGBlinkProtocol(RGBlinkProtocol * protocol, BYTE add, BYTE cmd, BYTE dat1, BYTE dat2, BYTE dat3, BYTE dat4)
{
	if(add >= MAX_RGBLNK_DEVICE)
	{
		printf("2. Address Error = %d\r\n", add);
		printf("cmd:%02x %02x %02x %02x %02x \r\n", cmd, dat1, dat2, dat3, dat4);
		return FALSE;
	}
	
	_send_cmd_rgblink(protocol, add, cmd,dat1,dat2,dat3,dat4);
    Task_RGBlinkProtocol(protocol);
	return TRUE;
}

int  SendCmdAndDataBlock_RGBlinkProtocol(RGBlinkProtocol * protocol, BYTE add, BYTE cmd, BYTE dat1, BYTE dat2, BYTE dat3, BYTE dat4, char *buffer,int size)
{
	u32 startTime;
	u8 checksum = 0;
	int i;
	
	if(add >= MAX_RGBLNK_DEVICE)
	{
		printf("3. Address Error = %d\r\n", add);
		printf("cmd:%02x %02x %02x %02x %02x \r\n", cmd, dat1, dat2, dat3, dat4);
		return FALSE;
	}
	_send_cmd_rgblink(protocol, add, cmd,dat1,dat2,dat3,dat4);
	// set block data checksum
	if(size > 0)
	{
		for(i=0; i<size-1; i++)
		{
			checksum += buffer[i];
		}
		buffer[size-1] = checksum;
		////////////////////////
		
		protocol->sendBuffer(buffer, size);
	}

	startTime = GetSysTick();
	
	// wait ack
	while((!canSendCmd_rgblink(protocol, add)) && protocol->isConnect[add])
	{
		Task_RGBlinkProtocol(protocol);
		if(isSysTickTimeout(startTime,3000))
		{
			printf("4. can't receive %d = %d\r\n", add, protocol->sendSn[add]);
			protocol->isConnect[add] = 0;
			return FALSE;
		}
	}
	if(protocol->isConnect[add] == FALSE)
	{
		while((!canSendCmd_rgblink(protocol, add)))
		{
			Task_RGBlinkProtocol(protocol);
			if(isSysTickTimeout(startTime,10))
			{
				printf("5. no connected %d = %d\r\n",  add, protocol->sendSn[add]);
				//protocol->isConnect[add] = 0;
				return FALSE;
			}
		}
	}
	return TRUE;
	
}




int SendCmd_RGBlinkProtocol(RGBlinkProtocol * protocol, BYTE add, BYTE cmd, BYTE dat1, BYTE dat2, BYTE dat3, BYTE dat4)
{
	u32 startTime;

	if(add >= MAX_RGBLNK_DEVICE)
	{
		printf("1. Address Error = %d\r\n", add);
		printf("cmd:%02x %.2x %02x %02x %02x \r\n", cmd, dat1, dat2, dat3, dat4);
		return FALSE;
	}
	
	_send_cmd_rgblink(protocol, add, cmd,dat1,dat2,dat3,dat4);

	startTime = GetSysTick();
	
	// wait ack
	while((!canSendCmd_rgblink(protocol, add)) && protocol->isConnect[add])
	{
		Task_RGBlinkProtocol(protocol);
		if(isSysTickTimeout(startTime,3000))
		{
			printf("1. can't receive %d = %d\r\n", add, protocol->sendSn[add]);
			protocol->isConnect[add] = 0;
			return FALSE;
		}
	}
	if(protocol->isConnect[add] == FALSE)
	{
		while((!canSendCmd_rgblink(protocol, add)))
		{
			Task_RGBlinkProtocol(protocol);
			if(isSysTickTimeout(startTime,10))
			{
				printf("2. no connected %d = %d\r\n",  add, protocol->sendSn[add]);
				//protocol->isConnect[add] = 0;
				return FALSE;
			}
		}
	}
	return TRUE;
	
}

int SendCmdIsAck_RGBlinkProtocol(RGBlinkProtocol * protocol, BYTE add)
{
	if(add >= MAX_RGBLNK_DEVICE)
	{
		printf("2. Address Error = %d\r\n", add);
		return FALSE;
	}
	if(protocol->receiveSn[add] == protocol->sendSn[add])
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

int WaitAck_RgblinkProtocol(RGBlinkProtocol * protocol, BYTE add, int timeout)
{
	u32 startTime;

	if(add >= MAX_RGBLNK_DEVICE)
	{
		printf("2. Address Error = %d\r\n", add);
		return FALSE;
	}

	startTime = GetSysTick();
	
	// wait ack
	while((protocol->receiveSn[add] != protocol->sendSn[add]))
	{
		Task_RGBlinkProtocol(protocol);
		if(isSysTickTimeout(startTime,timeout))
		{
			int i;
			printf("3. can't receive %d = %d\r\n",  add, protocol->sendSn[add]);
			protocol->isConnect[add] = 0;
			for(i=0; i<CMD_INDEX_SIZE; i++)  // clear receive buffer
			{
				protocol->receiveBuffer[i] = 0;
				protocol->receiveCmd[add][i] = 0;
			}
			return FALSE;
		}
		Sleep(1);
	}
	return TRUE;
}

// return -- FALSE , return dat1~dat4
int SendCmdAndWait_RGBlinkProtocol(RGBlinkProtocol * protocol, BYTE add, BYTE cmd, BYTE dat1, BYTE dat2, BYTE dat3, BYTE dat4)
{
	//int ack;
	if(add >= MAX_RGBLNK_DEVICE)
	{
		printf("3. Address Error = %d\r\n", add);
		return FALSE;
	}
	_send_cmd_rgblink(protocol,add, cmd,dat1,dat2, dat3,dat4);
	
	if(protocol->isConnect[add])
	{
		if(WaitAck_RgblinkProtocol(protocol,add, 3000) == FALSE)
		{
			return FALSE;
		}
	}
	else
	{
		if(WaitAck_RgblinkProtocol(protocol, add, 200) == FALSE)
		{
			return FALSE;
		}
	}
	return protocol->receiveCmd[add][CMD_INDEX_DAT1]+
		(protocol->receiveCmd[add][CMD_INDEX_DAT2]<<8)+
		(protocol->receiveCmd[add][CMD_INDEX_DAT3]<<16)+
		(protocol->receiveCmd[add][CMD_INDEX_DAT4]<<24);
		
}

void SendBuffer_RGBlinkProtocol(RGBlinkProtocol * protocol, char *buffer, int size)
{
	protocol->sendBuffer(buffer, size);
}

int ReceiveBuffer_RGBlinkProtocol(RGBlinkProtocol * protocol, char *buffer, int size, int timeout)
{
	u32 startTime;
	int i = 0;
	
	startTime = GetSysTick();
	
	while(1)
	{
		BYTE value;
		while(pipe_read(protocol->pipe, &value))
		{
			buffer[i++] = value;	
			if(i >= size)
			{
				return i;
			}
		}
		if(isSysTickTimeout(startTime, timeout))
		{
		    printf("ReceiveBuffer_RGBlinkProtocol %d byte\r\n", i);
			break;
		}
	}
	return i;
}








